﻿/// ****************************************************************************
/// Author:	        Robin Zhu
/// Create Date:	2013-7-30
/// Purpose: 		枚举扩展方法类，增加可用于枚举类型的方法
/// ****************************************************************************
/// Modify By		Date			Remark
/// Robin           2014-8-26       增加GetName扩展方法
/// Robin           2015-12-9       让GetName支持Flags
/// ****************************************************************************
/// 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;

namespace RB.Utility
{
    public static class EnumExtensions
    {
        /// <summary>
        /// 将一个Flags数组合并成一个Flags值
        /// </summary>
        /// <typeparam name="TEnum">Flags枚举类型</typeparam>
        /// <param name="values">枚举值数组</param>
        /// <returns>合并后的值</returns>
        public static TEnum Merge<TEnum>(IEnumerable<TEnum> values)
        {
            int result = 0;
            if (values != null)
            {
                foreach (TEnum value in values)
                {
                    result |= Convert.ToInt32(value);
                }
            }
            return (TEnum)Enum.ToObject(typeof(TEnum), result);
        }

        /// <summary>
        /// 得到枚举型的名称（如果使用了DisplayAttribute就用DisplayAttribute的名字)
        /// </summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <param name="eVal"></param>
        /// <returns></returns>
        public static string GetName<TEnum>(this TEnum eVal)
            where TEnum : struct, IConvertible
        {
            Type type = typeof(TEnum);
            if (type.IsEnum)
            {
                string name = eVal.ToString();
                if (Enum.IsDefined(type, eVal))
                {
                    name = GetDisplayName(type, name);
                }
                else
                {
                    bool isflag = type.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0;
                    if (isflag)
                    {
                        string[] names = name.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
                        for (int i = 0; i < names.Length; i++)
                        {
                            names[i] = GetDisplayName(type, names[i]);
                        }
                        name = string.Join(",", names);
                    }
                }
                return name;
            }
            else
            {
                throw new NotSupportedException("Only support enum type.");
            }
        }

        private static string GetDisplayName(Type type, string name)
        {
            var field = type.GetField(name);
            if (field != null)
            {
                var displayAttr = field.GetCustomAttributes(typeof(DisplayAttribute), false);
                if (displayAttr.Length > 0)
                {
                    name = ((DisplayAttribute)displayAttr[0]).GetName();
                }
            }
            return name;
        }

        public static IDictionary<TEnum, string> ToDictionary<TEnum>()
        {
            IDictionary<TEnum, string> result = new Dictionary<TEnum, string>();

            Type type = typeof(TEnum);
            bool isflag = type.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0;

            foreach (int value in Enum.GetValues(type))
            {
                if (!isflag || Bit1Count(value) == 1)
                {
                    TEnum eVal = (TEnum)Enum.ToObject(type, value);
                    string name = eVal.ToString();
                    var field = type.GetField(name);
                    var displayAttr = field.GetCustomAttributes(typeof(DisplayAttribute), false);
                    if (displayAttr.Length > 0)
                    {
                        name = ((DisplayAttribute)displayAttr[0]).Name;
                    }
                    result.Add(eVal, name);
                }
            }

            return result;
        }

        private static int Bit1Count(int value)
        {
            int num = 0;

            while (value != 0)
            {
                value &= (value - 1);
                num++;
            }
            return num;
        }

        public static int Bit1Count<T>(this T value)
            where T : struct, IComparable, IConvertible, IFormattable
        {
            return Bit1Count(value.ToInt32(null));
        }

        public static IEnumerable<KeyValuePair<TEnum, string>> GetOrderedNames<TEnum>()
        {
            SortedList<int, KeyValuePair<TEnum, string>> orders = new SortedList<int, KeyValuePair<TEnum, string>>();

            Type type = typeof(TEnum);
            bool isflag = type.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0;

            foreach (int value in Enum.GetValues(type))
            {
                if (!isflag || Bit1Count(value) == 1)
                {
                    TEnum eVal = (TEnum)Enum.ToObject(type, value);
                    string name = eVal.ToString();
                    var field = type.GetField(name);
                    var displayAttr = field.GetCustomAttributes(typeof(DisplayAttribute), false);

                    int order = 0;
                    if (displayAttr.Length > 0)
                    {
                        name = ((DisplayAttribute)displayAttr[0]).Name;
                        order = ((DisplayAttribute)displayAttr[0]).Order;
                    }
                    orders.Add(order, new KeyValuePair<TEnum, string>(eVal, name));
                }
            }

            return orders.Values;
        }

        public static IEnumerable<KeyValuePair<TEnum, string>> GetOrderedDescriptions<TEnum>()
        {
            SortedList<int, KeyValuePair<TEnum, string>> orders = new SortedList<int, KeyValuePair<TEnum, string>>();

            Type type = typeof(TEnum);
            bool isflag = type.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0;

            foreach (int value in Enum.GetValues(type))
            {
                if (!isflag || Bit1Count(value) == 1)
                {
                    TEnum eVal = (TEnum)Enum.ToObject(type, value);
                    string name = eVal.ToString();
                    var field = type.GetField(name);
                    var displayAttr = field.GetCustomAttributes(typeof(DisplayAttribute), false);

                    int order = 0;
                    if (displayAttr.Length > 0)
                    {
                        name = ((DisplayAttribute)displayAttr[0]).Description;
                        order = ((DisplayAttribute)displayAttr[0]).Order;
                    }
                    orders.Add(order, new KeyValuePair<TEnum, string>(eVal, name));
                }
            }

            return orders.Values;
        }
    }
}
